#include "image.h"

#include <cassert>

Image::Image()
    : BaseData()
    , Width(0)
    , Height(0)
    , Data(0)
{
}

Image::Image(size_t width, size_t height)
    : BaseData(new float[width * height + 4])
    , Width(width)
    , Height(height)
    , Data(BaseData.get() + ((size_t) BaseData.get() % 16)/4)
{
}

Image::Image(float* ptr, size_t width, size_t height)
    : BaseData()
    , Width(width)
    , Height(height)
    , Data(ptr)
{
}

Image::Image(Image&& image)
    : BaseData(std::move(image.BaseData))
    , Width(std::move(image.Width))
    , Height(std::move(image.Height))
    , Data(std::move(image.Data))
{
}

Image& Image::operator=(Image&& image)
{
    BaseData = std::move(image.BaseData);
    Width = std::move(image.Width);
    Height = std::move(image.Height);
    Data = std::move(image.Data);
    return *this;
}

Image& Image::operator+=(const Image& image) {
    assert(Width == image.Width && Height == image.Height);

    for (size_t i = 0; i < Width * Height; ++i) {
        Data[i] += image.Data[i];
    }

    return *this;
}

Image& Image::operator/=(float v) {
    for (size_t i = 0; i < Width * Height; ++i) {
        Data[i] /= v;
    }

    return *this;
}

Image Image::DeepCopy() const {
    Image result(Width, Height);
    for (size_t i = 0; i < Width * Height; ++i) {
        result.Data[i] = Data[i];
    }
    return result;
}

Image Image::Zeros(size_t width, size_t height) {
    Image result(width, height);
    for (size_t i = 0; i < width * height; ++i) {
        result.Data[i] = 0;
    }
    return result;
}

Image Image::Ones(size_t width, size_t height) {
    Image result(width, height);
    for (size_t i = 0; i < width * height; ++i) {
        result.Data[i] = 1;
    }
    return result;
}

bool Image::IsEmpty() const {
    return Width == 0 && Height == 0;
}

Image SqrSobelH(const Image& input) {
    Image output(input.Width, input.Height);

    const float* inPtr = input.Data + input.Width;
    float* outPtr = output.Data + output.Width;

    for (size_t i = 1; i < input.Height - 1; ++i) {
        *outPtr = 0;
        ++inPtr; ++outPtr;
        for (size_t j = 1; j < input.Width - 1; ++j, ++inPtr, ++outPtr) {
            *outPtr = inPtr[-1] - inPtr[1];
            *outPtr *= *outPtr * 0.25;
        }
        *outPtr = 0;
        ++inPtr; ++outPtr;
    }

    // first line
    outPtr = output.Data;
    for (size_t i = 0; i < output.Width; ++i) {
        outPtr[i] = 0;
    }

    // last line
    outPtr = output.Data + output.Width * (output.Height - 1);
    for (size_t i = 0; i < output.Width; ++i) {
        outPtr[i] = 0;
    }

    return output;
}

Image SqrSobelV(const Image& input) {
    Image output(input.Width, input.Height);
    
    const float* topPtr = input.Data;
    float* ptr = output.Data + output.Width;
    const float* botPtr = input.Data + input.Width * 2;
    
    for (size_t i = 1; i < input.Height - 1; ++i) {
        *ptr = 0;
        ++ptr; ++topPtr; ++botPtr;
        for (size_t j = 1; j < input.Width - 1; ++j, ++ptr, ++topPtr, ++botPtr) {
            *ptr = *topPtr - *botPtr;
            *ptr *= *ptr * 0.25;
        }
        *ptr = 0;
        ++ptr; ++topPtr; ++botPtr;
    }

    // first line
    ptr = output.Data;
    for (size_t i = 0; i < input.Width; ++i) {
        ptr[i] = 0;
    }

    // last line
    ptr = output.Data + output.Width * (output.Height - 1);
    for (size_t i = 0; i < output.Width; ++i) {
        ptr[i] = 0;
    }

    return output;
}

void ToIntegral(Image& image) {
    float* ptr = image.Data + image.Width;
    float sum = *image.Data;
    for (size_t i = 1; i < image.Height; ++i, ptr += image.Width) {
        sum = *ptr += sum;
    }

    ptr = image.Data;
    for (size_t j = 1; j < image.Width; ++j) {
        ptr[j] += ptr[j-1];
    }

    float* prevLine = image.Data;
    float* thisLine = image.Data + image.Width;
    for (size_t i = 1; i < image.Height; ++i, prevLine += image.Width, thisLine += image.Width) {
        for (size_t j = 1; j < image.Width; ++j) {
            thisLine[j] += thisLine[j - 1] + prevLine[j] - prevLine[j - 1];
        }
    }    
}
